﻿using System;
using System.Collections.Generic;
using System.Text;
using SysConsole = System.Console;

namespace LSHoude.Console
{
    /// <summary>
    /// Display a console progress bar as follows:
    /// [******     ]80%
    /// Status message
    /// </summary>
    /// <example>
    /// ProgressBar pBar = new ProgressBar();
    /// for (int i = 1; i <= 999; i++)
    /// {
    ///     pb.Progress = i / 999 * 100;
    ///     System.Threading.Thread.Sleep(new TimeSpan(0,0,0,0,100));
    /// }
    /// pb.Dispose();
    /// </example>
    public class ProgressBar:IDisposable
    {
        private const short CONSOLE_WIDTH = 80;
        // Console width - two enclosing brackets - 3 pourcentage numbers - pourcent symbol - one space.
        private const short NUMBER_OF_STEPS = CONSOLE_WIDTH - 7;
        private readonly int _progressBarYPosition;
        private readonly int _statusMessageYPosition;
        private int _previousProgress;

        private int _progressInPercentage;
        public int ProgressInPercentage
        {
            get { return _progressInPercentage; }
            set {
                if (value < 0 || value > 100)
                {
                    throw new ArgumentException(
                        string.Format("The progress in pourcentage must be between 0 and 100. {0} given.", value)
                    );
                }
                _previousProgress = _progressInPercentage;
                _progressInPercentage = value;
                Draw(_previousProgress, _progressInPercentage, _progressBarYPosition);
            }
        }

        private string _statusMessage;
        public string StatusMessage
        {
            get { return _statusMessage; }
            set {
                bool statusChanged = _statusMessage != value;
                if (statusChanged)
                {
                    _statusMessage = value;
                    SysConsole.SetCursorPosition(0, _statusMessageYPosition);
                    SysConsole.Write(value);
                }
            }
        }
        
        public ProgressBar()
        {
            _progressBarYPosition = SysConsole.CursorTop;
            _statusMessageYPosition = SysConsole.CursorTop + 1;
        }

        private static void Draw(int previousProgress, int progressInPercentage, int yPosition)
        {
            int stepsDone = ComputeStepsDone(progressInPercentage);
            int previousStepsDone = ComputeStepsDone(previousProgress);
            
            // Don't refresh progress bar if we are at the same step.
            if (stepsDone == previousStepsDone)
            {
                return;
            }

            SysConsole.SetCursorPosition(0, yPosition);
            SysConsole.Write("[");
            for (int i = 0; i < NUMBER_OF_STEPS; i++)
            {
                if (i < stepsDone)
                {
                    SysConsole.Write("*");
                }
                else
                {
                    SysConsole.Write(" ");
                }
            }
            SysConsole.Write("]{0}%", progressInPercentage);

        }

        /// <summary>
        /// Put the cursor below the progress bar.
        /// </summary>
        public void Dispose()
        {
            SysConsole.WriteLine();
        }

        /// <summary>
        /// Compute the number of steps done.
        /// </summary>
        /// <param name="progressInPercentage"></param>
        /// <returns></returns>
        /// <example>
        /// If CONSOLE_WIDTH = 80 and NUMBER_OF_STEPS = 73
        /// ComputeStepsDone(99) = 72
        /// ComputeStepsDone(100) = 73
        /// </example>
        private static int ComputeStepsDone(int progressInPercentage)
        {
            return NUMBER_OF_STEPS * progressInPercentage / 100;
        }
    }
}
